// Font class
// 
// PLAN: 1. support zooming -OB

#include <stdio.h>
#include <stdlib.h>         // for malloc
#include <string.h>         // for memcpy
#include "Common/Debug.h"
#include "Common/Macros.h"
#include "Common/FileSys.h"
#include "Common/Memory.h"
#include "Color.h"
#include "Draw.h"
#include "Font.h"

#define ZOOM_SUPPORT    2   // scale for zoom, feature later -OB

typedef struct 
{
    UInt16  TotalNum;
	UInt8   Flags;		    // bit0:	1 for variant width else 0 for fixed width
							// bit1~7:	not used
	UInt8   BitsPerPixel;   // how many bits used to present a pixel
	UInt8	Width;          // width in pixels, when fonts are in variant width
                            // this is the max width of all
    UInt8	Height;
    UInt16  _reserved;		// not used
}	FONT_FILE_HEADER;

/////////////////////////////////////////////////////////////////////////////
// Advanced font cache from charlie 20060916

#define __FONT_CACHE_USE_PAGE_SWAP__	1		// enable this functiono not?

#define MAX_FONT_CACHE_MEM_SIZE			1024*30

#if __FONT_CACHE_USE_PAGE_SWAP__
// Create a font Cache
BOOL g_CreateFontCache(FONT_FILE_HEADER fh, TFont* font)
{
  	unsigned long offset1 = 0, offset2 = 0;
  	int i = 0;
  	unsigned int page_num = 0, page_size = 0, cache_size = 0, font_size = 0, max_font_size = 0;
	int len = 0;
		
	if (fh.Flags & 1) // has variant width table, load these table and find max font size
	{
		// first move file position to 8 (just after FONT_FILE_HEADER)
		fs_Seek(font->pFile, sizeof(FONT_FILE_HEADER), SEEK_SET);

		// load width table and offset table
		len = font->TotalNum * (sizeof(UInt8) + sizeof(UInt32));	// width table + offset table
		
		font->pWidthTbl = (UInt8*) ut_malloc(len);

	    if (font->pWidthTbl == NULL)
		{
			TRACE("FontCreate: memory failed! (width tbl %d)\n", len);
   			g_DestroyFont(font);
       		return FALSE;		// failed! memory not enough
		}
		font->pOffsetTbl = (UInt32 *) (font->pWidthTbl + (font->TotalNum * sizeof(UInt8)));

		fs_Read(font->pWidthTbl, len, font->pFile);

		// find the max size of font by comparison of offsets
		for(i = 0; i < font->TotalNum; i++)
		{
			if (i == 0) // first offset
			{	
				offset1 = font->pOffsetTbl[i];
			}
			else if (i == font->TotalNum -1) // last offset
			{
				offset2 = fs_GetFileLen(font->pFile);
				font_size = offset2 - offset1;
				if (font_size > max_font_size)
				{
					max_font_size = font_size;
					TRACE("Find larger font size.\n");
				}
			}
			else // other offsets in the middle of offset table
			{
				offset2 = font->pOffsetTbl[i];
				font_size = offset2 - offset1;
				if (font_size > max_font_size)
				{
					max_font_size = font_size;
					TRACE("Find larger font size.\n");
				}
				offset1 = offset2; // assign offset1 to current offset for next comparison
			}
		}
			
		// assign page size with the max font size get from offset comparison
		page_size = max_font_size;
	}
	else // fixed width
	{
		// assign page size with the const number from FONT_FILE_HEADER
		page_size = font->PackedSize;
	}

	// according to page_size, calculate page_num, cache_size
	page_num = font->TotalNum / 5;		// cache 1/5 fonts
	cache_size = page_size * page_num;	// calculate needed cache size
	cache_size = min(cache_size, MAX_FONT_CACHE_MEM_SIZE); // compare with our desired memory usage, get the smaller one
	page_num = cache_size / page_size;	// use memory usage limitation got above, calculate page num allowed
	cache_size = page_num * page_size;	// calculate real memory usage
		
	// assign cache attributes
	font->pFontCache->PageNum = page_num; // total page number
	font->pFontCache->PageUsed = 0; 
	font->pFontCache->PageSize = page_size;
	
	// allocate page_infos
	font->pFontCache->pPageInfo = (FontPageInfo*) ut_malloc ( sizeof(FontPageInfo) * page_num );
	if (font->pFontCache->pPageInfo == NULL)
	{
        TRACE("FontCreate: memory failed! (pCache->pPageInfo buff)\n");
    	g_DestroyFont(font);
        return FALSE;           // failed! memory not enough
	}
	
	// initialize page_infos
	for (i = 0; i< (int)page_num; i++) 
	{
		font->pFontCache->pPageInfo[i].serial = -1; // not used
		font->pFontCache->pPageInfo[i].Hit = 0; 
		font->pFontCache->pPageInfo[i].page_id = -1;
	}
	
	// allocate pages
	font->pFontCache->pPage = (char*) ut_malloc( page_size * page_num);
	if (font->pFontCache->pPage == NULL)
	{
        TRACE("FontCreate: memory failed! (pCache buff)\n");
    	g_DestroyFont(font);
        return FALSE;           // failed! memory not enough
	}
	
	// initialize pages
	memset(font->pFontCache->pPage, 0, page_size * page_num);

	return TRUE;
}

long g_LoadFontFromCache(const TFont *font, int serial)
{
    // find the offset in pCache (directly load from pCache or open font file)
    // and the width and height for pBitmap

	int	least_hit, least_hit_page_info_id, page_info_id=-1, page_id=-1;
	FILE* fp;
	long offset;
	int i;
	int page_num, page_used, page_size, bs_l, bs_r, bs_m; 
	
	page_num = font->pFontCache->PageNum;
	page_used = font->pFontCache->PageUsed;
	page_size = font->pFontCache->PageSize;
	
	// search used page for serial, use binary search here
	bs_l = 0;
	bs_r = page_used-1;
	
	while(bs_l <= bs_r)
	{	
		bs_m = (bs_l + bs_r)/2;	// find the mid entry of used pPageInfo
	
		if (serial > font->pFontCache->pPageInfo[bs_m].serial)
		{
			bs_l = bs_m + 1;
		}
		else if (serial < font->pFontCache->pPageInfo[bs_m].serial)
		{
			bs_r = bs_m - 1;
		}
		else
		{
			page_info_id = bs_m; 
			break;
		}
	}
/*	
	for (i = 0; i < font->pFontCache->PageUsed; i++)
	{
		if (font->pFontCache->pPageInfo->serial == serial) // found, font cached in page_id i
		{	
			page_id = i;
			break;
		}
	}
*/
	if (page_info_id != -1)		// found in cache
	{
		font->pFontCache->pPageInfo[page_info_id].Hit += 1; // increase hit count in page_info
		page_id = font->pFontCache->pPageInfo[page_info_id].page_id; // get page_id
		//offset = font->pFontCache->pPage + page_size*page_id; // get the start address
	} 
	else // not found in cache
	{
		// find a space (page_id) in pPage for storing font data which will be loaded from file
		// and a suitable position (page_info_id) in pPageInfo for storing page_info
		
		if (font->pFontCache->PageUsed < font->pFontCache->PageNum ) // still have empty page
		{
			// find a suitable position in pPageInfo
			for(i = 0; i<page_used; i++)
			{
				if (serial < font->pFontCache->pPageInfo[i].serial) // found the position
				{	
					page_info_id = i;		// set page_info_id to i
					break;
				}
			}

			if (page_info_id == -1)			// all serials contained in pPageInfo are smaller than this one, no need to move page_infos
			{
				page_info_id = page_used;	// page_info_id got!
			}
			else 
			{   
			    // move page_infos 1 page forward from position page_info_id
				for(i = page_used-1; i>= page_info_id ; i--)  // move from tail
				{
					memcpy( (FontPageInfo*) &(font->pFontCache->pPageInfo[i+1]) , \
							(FontPageInfo*) &(font->pFontCache->pPageInfo[i]), sizeof(FontPageInfo));
				}
				
			}

			page_id = page_used; // set page_id to page_used (0 ~ page_used-1 are used), page_id got!
			
			// adjust PageUsed
			font->pFontCache->PageUsed += 1;
		}
		else // all pages used, must find a page with rarest hit
		{
			// first, find the page with rarest hit
			for (i = 0; i<page_used;i++)
			{
				if (i == 0)
				{   // assign the first value for following comparison
					least_hit_page_info_id = i;
					least_hit = font->pFontCache->pPageInfo[i].Hit;
				}
				else
				{
					if ((int)font->pFontCache->pPageInfo[i].Hit < least_hit) // page hit comparison
					{	
						least_hit_page_info_id = i;
						least_hit = font->pFontCache->pPageInfo[i].Hit;
					}	
				}
			} 
			// after scan, we got a page which can be dropped
			
			// load page_id
			page_id = font->pFontCache->pPageInfo[least_hit_page_info_id].page_id;  // page_id got!
			
			// then, find a suitable position to store page_info
			for(i = 0; i<page_num; i++)
			{
				if (serial < font->pFontCache->pPageInfo[i].serial) // found the position
				{	
					page_info_id = i; // set page_info_id to i
					break;
				}
			}
			if (page_info_id == -1) // new serial is larger than exist serials
			{
				page_info_id = page_num -1; // set position to tail
			}
			// page_info_id got!
			
			// move page_infos between page_info_id and least_hit_page_info_id
			if (page_info_id < least_hit_page_info_id)  // move page_infos forward
			{
				for (i = least_hit_page_info_id-1; i>=page_info_id; i--)
				{
					memcpy( (FontPageInfo*) &( font->pFontCache->pPageInfo[i+1]), \
							(FontPageInfo*) &( font->pFontCache->pPageInfo[i] ), sizeof(FontPageInfo));
				}
			}
			else if( page_info_id > least_hit_page_info_id) // move page_infos backward
			{
				for (i = least_hit_page_info_id+1; i<=page_info_id; i++)
				{
					memcpy( (FontPageInfo*) &( font->pFontCache->pPageInfo[i-1] ), \
							(FontPageInfo*) &( font->pFontCache->pPageInfo[i] ), sizeof(FontPageInfo));
				}
			}
			else // equals, nothing need to do
			{
			}
			
		}

		// now we already got page_info_id and page_id
		// set page attributes in pPageInfo[page_info_id]
		font->pFontCache->pPageInfo[page_info_id].serial = serial;
		font->pFontCache->pPageInfo[page_info_id].Hit = 1;
		font->pFontCache->pPageInfo[page_info_id].page_id = page_id;
		// clear memory of pPage[page_id]
		memset(font->pFontCache->pPage + page_size*page_id, 0, page_size);
		
		// load font from file and store to page
		if (font->pWidthTbl) // varient font width
		{
			offset = font->pOffsetTbl[serial]; // Note: offset in file, not the same with returned 'offset'
		}
		else
		{
			offset = sizeof(FONT_FILE_HEADER) + font->PackedSize * serial; // Note: offset in file, not the same with returned 'offset'
		}
		
		fp = fs_Open(font->pFontCache->filename, "rb");
		if (fp == NULL)
		{
			TRACE("Font file open fail.\n");
			return 0;
		}
		// to do: check fs_Open result
		fs_Seek(fp, offset, SEEK_SET);
		fs_Read((char*) ( font->pFontCache->pPage + font->pFontCache->PageSize * page_id ), font->pFontCache->PageSize, fp);
		fs_Close(&fp);
		
	}

	// calculate start address of font data
	offset = (long) font->pFontCache->pPage + page_size * page_id; // address of font data in pPage

	return offset;
}
#endif

// Font functions
/* Create a font object */
int  g_CreateFont(TFont *font, const char *filename, int loadToMemory)
{
	FONT_FILE_HEADER fh;
    int len;
  
    memset(font, 0, sizeof(TFont));

    // Load character fonts
    if ((font->pFile = fs_Open(filename, "rb")) == NULL)
    {
        TRACE("FontCreate: file '%s' not found!\n", filename);

        return 0;           // failed! file not found
    }
        
    ASSERT_DEBUG(sizeof(FONT_FILE_HEADER) == 8);

	fs_Read(&fh, sizeof(FONT_FILE_HEADER), font->pFile);

	if (fh.BitsPerPixel != 1)
	{
        TRACE("FontCreate: unsupported bbp!\n");

    	g_DestroyFont(font);

        return 0;           // failed! wrong format
	}
	
    font->TotalNum = fh.TotalNum;  
    font->Width    = fh.Width;			// font width(max) in pixels
    font->Height   = fh.Height;			// font height in pixels 
    	
	// Bitmap max size in pixels for drawing a font
	// NOTE: considering all bold/outline/zoom.. effects
    len = (font->Width + 3) * (font->Height + 2) * ZOOM_SUPPORT ;	

    // Allocate memory for bitmap struct
    font->pBitmap = (TImage *) ut_malloc(len * PIXEL_SIZE  + sizeof(UInt16) * 2);

    if (font->pBitmap == NULL)
    {
        TRACE("FontCreate: memory failed! (bitmap buff)\n");

    	g_DestroyFont(font);

        return 0;           // failed! memory not enough
    }

    font->pBitmap->width = font->Width; 	// this is the BMP default width in pixels
    font->pBitmap->height = font->Height;   // and height

    // lengths for loading native font data (bits packed as pixel by pixel)

    len = font->Width * font->Height * fh.BitsPerPixel;	// length by bits

    font->PackedSize = (len + 7) / 8;					// at least bytes to store these bits

    font->OctanSize  = len >> 3;	//len / 8;			// how many full 8 bits
    font->Exceeding  = len & 7;		//len % 8;			// exceeding bits

	// File length
    len = fs_GetFileLen(font->pFile) - sizeof(FONT_FILE_HEADER);

	// Load all fonts from file to memory
    if (loadToMemory)
    {
#if __FONT_CACHE_USE_PAGE_SWAP__
		// create a FontCache object
		font->pFontCache = (FontCache*) ut_malloc(sizeof(FontCache));
		
		// save filename for later access
		font->pFontCache->filename = (char*) ut_malloc(strlen(filename) +1);
		strncpy(font->pFontCache->filename, filename, strlen(filename));
		font->pFontCache->filename[strlen(filename)] = 0;

		// create and set other members of font cache
		if (g_CreateFontCache(fh, font) == FALSE)
		{
			TRACE("FontCreate: memory failed! (FontCache buff)\n");
			g_DestroyFont(font);
			fs_Close_(font->pFile);
			font->pFile = NULL;
			return 0;
		}
		
		fs_Close_(font->pFile);

		font->pFile = NULL;
#else
        // allocate for all fonts
        font->pCache = (char *) ut_malloc(len);

        if (font->pCache == NULL)
    	{
            TRACE("FontCreate: memory failed! (cache %d)\n", len);

    		g_DestroyFont(font);

        	return 0;			// failed! memory not enough
    	}
   
        fs_Read(font->pCache, sizeof(char) * len, font->pFile);

        fs_Close_(font->pFile);

        font->pFile = NULL;		// close file handle so means all fonts loaded

		// has variant width table
		if (fh.Flags & 1)
		{
			font->pWidthTbl = (UInt8*)font->pCache;	// let pWidthTbl point to cache head
				
			font->pOffsetTbl = (UInt32*) (font->pWidthTbl + (font->TotalNum * sizeof(UInt8)));

			// NOTE: make sure the address must be even aligned otherwise it will has
			//		 a memory fault in PL1061 target board when drawing this font
			ASSERT_DEBUG(((UInt32) (font->pOffsetTbl) & 1) == 0);
		}
#endif
    }
	// use file handle for loading fonts in time
    else    
    {
		// only allocate one font buffer
    	font->pCache = (char *) ut_malloc(font->PackedSize);

    	if (font->pCache == NULL)
    	{
            TRACE("FontCreate: memory failed! (packed %d)\n", font->PackedSize);

    		g_DestroyFont(font);

        	return 0;			// failed! memory not enough
    	}

		// has variant width table
		if (fh.Flags & 1)
		{
			len = font->TotalNum * (sizeof(UInt8) + sizeof(UInt32));	// width table + offset table

			font->pWidthTbl = (UInt8*) ut_malloc(len);

		    if (font->pWidthTbl == NULL)
			{
				TRACE("FontCreate: memory failed! (width tbl %d)\n", len);

    			g_DestroyFont(font);

        		return 0;		// failed! memory not enough
			}

			font->pOffsetTbl = (UInt32 *) (font->pWidthTbl + (font->TotalNum * sizeof(UInt8)));

			fs_Read(font->pWidthTbl, len, font->pFile);
		}
    }

    font->FGColor = _RGB(255,255,255);  // white
    font->BGColor = _RGB(0,0,0);        // black
    font->GLColor = _RGB(128,128,128);	// gray
    font->Effects = FONT_FX_NORMAL;

    return font->TotalNum;
}

//Bill test
// Font functions
/* Create a font object */
int  g_CreateAsciiFont(TFont *font, const char *filename, int loadToMemory)
{
	FONT_FILE_HEADER fh;
    int len;
  
    memset(font, 0, sizeof(TFont));

    // Load character fonts
    if ((font->pFile = fs_Open(filename, "rb")) == NULL)
    {
        TRACE("FontCreate: file '%s' not found!\n", filename);

        return 0;           // failed! file not found
    }
        
    ASSERT_DEBUG((int)sizeof(FONT_FILE_HEADER) == 8);

	fs_Read(&fh, sizeof(FONT_FILE_HEADER), font->pFile);

	if (fh.BitsPerPixel != 1)
	{
        TRACE("FontCreate: unsupported bbp!\n");

    	g_DestroyFont(font);

        return 0;           // failed! wrong format
	}
	
    font->TotalNum = fh.TotalNum;  
    font->Width    = fh.Width;			// font width(max) in pixels
    font->Height   = fh.Height;			// font height in pixels 
    	
	// Bitmap max size in pixels for drawing a font
	// NOTE: considering all bold/outline/zoom.. effects
    len = (font->Width + 3) * (font->Height + 2) * ZOOM_SUPPORT ;	

    // Allocate memory for bitmap struct
    font->pBitmap = (TImage *) ut_malloc(len * PIXEL_SIZE  + sizeof(UInt16) * 2);

    if (font->pBitmap == NULL)
    {
        TRACE("FontCreate: memory failed! (bitmap buff)\n");

    	g_DestroyFont(font);

        return 0;           // failed! memory not enough
    }

    font->pBitmap->width = font->Width; 	// this is the BMP default width in pixels
    font->pBitmap->height = font->Height;   // and height

    // lengths for loading native font data (bits packed as pixel by pixel)

    len = font->Width * font->Height * fh.BitsPerPixel;	// length by bits

    font->PackedSize = (len + 7) / 8;					// at least bytes to store these bits

    font->OctanSize  = len >> 3;	//len / 8;			// how many full 8 bits
    font->Exceeding  = len & 7;		//len % 8;			// exceeding bits

	// File length
    len = fs_GetFileLen(font->pFile) - sizeof(FONT_FILE_HEADER);

	// Load all fonts from file to memory
    if (loadToMemory)
    {

        // allocate for all fonts
        font->pCache = (char *) ut_malloc(len);

        if (font->pCache == NULL)
    	{
            TRACE("FontCreate: memory failed! (cache %d)\n", len);

    		g_DestroyFont(font);

        	return 0;			// failed! memory not enough
    	}
   
        fs_Read(font->pCache, sizeof(char) * len, font->pFile);

        fs_Close_(font->pFile);

        font->pFile = NULL;		// close file handle so means all fonts loaded

		// has variant width table
		if (fh.Flags & 1)
		{
			font->pWidthTbl = (UInt8*)font->pCache;	// let pWidthTbl point to cache head
				
			font->pOffsetTbl = (UInt32*) (font->pWidthTbl + (font->TotalNum * sizeof(UInt8)));

			// NOTE: make sure the address must be even aligned otherwise it will has
			//		 a memory fault in ucLinux target board when drawing this font
			ASSERT_DEBUG(((UInt32) (font->pOffsetTbl) & 1) == 0);
		}
    }
	// use file handle for loading fonts in time
    else    
    {
		// only allocate one font buffer
    	font->pCache = (char *) ut_malloc(font->PackedSize);

    	if (font->pCache == NULL)
    	{
            TRACE("FontCreate: memory failed! (packed %d)\n", font->PackedSize);

    		g_DestroyFont(font);

        	return 0;			// failed! memory not enough
    	}

		// has variant width table
		if (fh.Flags & 1)
		{
			len = font->TotalNum * (sizeof(UInt8) + sizeof(UInt32));	// width table + offset table

			font->pWidthTbl = (UInt8*) ut_malloc(len);

		    if (font->pWidthTbl == NULL)
			{
				TRACE("FontCreate: memory failed! (width tbl %d)\n", len);

    			g_DestroyFont(font);

        		return 0;		// failed! memory not enough
			}

			font->pOffsetTbl = (UInt32 *) (font->pWidthTbl + (font->TotalNum * sizeof(UInt8)));

			fs_Read(font->pWidthTbl, len, font->pFile);
		}
    }

    font->FGColor = _RGB(255,255,255);  // white
    font->BGColor = _RGB(0,0,0);        // black
    font->GLColor = _RGB(128,128,128);	// gray
    font->Effects = FONT_FX_NORMAL;

    return font->TotalNum;
}

/* Destroy a font object */
void g_DestroyFont(TFont *font)
{
    SAFE_FREE(font->pBitmap);
    
    if (font->pFile != NULL)
    {
        fs_Close_(font->pFile);
        font->pFile = NULL;
    }
    
	if (font->pWidthTbl)
	{
		SAFE_FREE(font->pWidthTbl);	// NOTE: pWidthTbl already been the memory head of cache
									// so don't need to free pCache
	}
	else
		SAFE_FREE(font->pCache);
}

/* Get the width of special char */
int  g_GetFontWidth(const TFont *font, int serial)
{
	if (font->pWidthTbl == NULL)
		return font->Width;	// fixed width
	else if (serial < 0 || serial >= font->TotalNum) 
		return 0;			// out of the scope
	
	return font->pWidthTbl[serial];
}

/* Get the height of the font */
int  g_GetFontHeight(const TFont *font)
{
    return font->Height;
}

/* Draw the special char on canvas */
int g_DrawFont(const TCanvas *canvas, const TFont *font, int x, int y, int serial)
{
    long offset;
    UInt8 *pack, bits;
	int fg, bg;
    int i;

	int octanSize;
	int exceeding;

#if defined __RGB16
    
	UInt16 *data;       // point to the data of bitmap

	#define SET_PIXEL_BY_BIT(bit)	\
		(*(data++) = (UInt16) ((bits & (bit)) ? fg : bg))

#elif defined __RGB24

	UInt8 *data; 
	UInt8 fg_a, fg_b, fg_c, bg_a, bg_b, bg_c;

	#define SET_PIXEL_BY_BIT(bit)									\
		if (bits & (bit))											\
		{ *(data++) = fg_a; *(data++) = fg_b; *(data++) = fg_c; }	\
		else														\
		{ *(data++) = bg_a; *(data++) = bg_b; *(data++) = bg_c; }

#endif	

//	ASSERT_DEBUG(canvas->Type && font->pBitmap);

    // Verify the serial number
    if (serial < 0 || serial >= font->TotalNum) 
    {
        TRACE("FontDraw: out of the scope 0-%d! (%d)\n", font->TotalNum - 1, serial);
        return 0;
    }

    // Load packed font data to buffer
#if __FONT_CACHE_USE_PAGE_SWAP__
    // find the offset in pCache (directly load from pCache or open font file)
    // and the width and height for pBitmap

	offset = g_LoadFontFromCache(font, serial);

	if (offset == 0) // load font fail
	{
		TRACE("FontDraw: load font (serial: %d) from file to page fail.\n");
		return 0;
	}

	// set pBitmap width and height
	if (font->pWidthTbl)
	{
		font->pBitmap->width  = font->pWidthTbl[serial];
		font->pBitmap->height = font->Height;
	}
	else
	{
		font->pBitmap->width  = font->Width;
		font->pBitmap->height = font->Height;
	}
#else
	if (font->pWidthTbl)
	{
		offset = font->pOffsetTbl[serial];

		font->pBitmap->width  = font->pWidthTbl[serial];
		font->pBitmap->height = font->Height;
	}
	else
	{
		offset = (long) serial * font->PackedSize;

		font->pBitmap->width  = font->Width;
		font->pBitmap->height = font->Height;
	}

    if (font->pFile != NULL)
    {
        // From font file
        fs_Seek(font->pFile, sizeof(FONT_FILE_HEADER) + offset, SEEK_SET);
        fs_Read(font->pCache, sizeof(char) * font->PackedSize, font->pFile);

        offset = 0;		// so later access from pCache
    }
#endif

	if (GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{
		fg = font->GLColor;
		x--;
	}
	else
		fg = font->FGColor;

	if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
		bg = font->BGColor;
	else
		bg = fg ^ 0xC0C0;

#ifdef __RGB24
	fg_a = ((UInt8)((fg) >> 16));
	fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
	fg_c = ((UInt8)(fg));
	bg_a = ((UInt8)((bg) >> 16));
	bg_b = ((UInt8)(((UInt16)(bg)) >> 8));
	bg_c = ((UInt8)(bg));
#endif

redraw:

    // Unpack this font to pBitmap
#if __FONT_CACHE_USE_PAGE_SWAP__
	pack = (UInt8 *)offset; // return an real offset
#else
	pack = (UInt8 *)font->pCache + offset;
#endif

#if defined __RGB16
    data = (UInt16*) font->pBitmap->data;
#elif defined __RGB24
    data = font->pBitmap->data;
#endif

	if (font->pWidthTbl)
	{
		i = font->pBitmap->width * font->Height;		// length by bits

		octanSize  = i >> 3;	// i / 8;				// how many full 8 bits
		exceeding  = i & 7;		// i % 8;				// exceeding bits
	}
	else
	{
		octanSize  = font->OctanSize;
		exceeding  = font->Exceeding;
	}

	// every full 8 bits
    for (i = 0; i < octanSize; i++)
    {
        bits = *(pack++);

        // Test each bit patterns
		SET_PIXEL_BY_BIT(0x80);
		SET_PIXEL_BY_BIT(0x40);
		SET_PIXEL_BY_BIT(0x20);
		SET_PIXEL_BY_BIT(0x10);
		SET_PIXEL_BY_BIT(0x08);
		SET_PIXEL_BY_BIT(0x04);
		SET_PIXEL_BY_BIT(0x02);
		SET_PIXEL_BY_BIT(0x01);
    }
	// and remained bits
    if (exceeding)
    {
        bits = *pack;
		
        // Test each bit patterns
		for (i = 7; i > 7 - exceeding; i--)
        {
			SET_PIXEL_BY_BIT(1 << i);
        }
    }

    // Draw this pBitmap to canvas
    if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
        g_DrawImage(canvas, x, y, font->pBitmap); 
    else
        g_DrawImageTrans(canvas, x, y, font->pBitmap, bg); 

	i = GET_BIT(font->Effects, FONT_FX_BOLD) ? 1 : 0;

	if (fg != font->FGColor) // && GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{		
		g_DrawImageTrans(canvas, x + 1, y - 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 1, y + 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 2 + i, y, font->pBitmap, bg); 
		if (i)
		{
			g_DrawImageTrans(canvas, x + 2, y - 1, font->pBitmap, bg); 
			g_DrawImageTrans(canvas, x + 2, y + 1, font->pBitmap, bg); 
		}

		fg = font->FGColor;

#ifdef __RGB24
		fg_a = ((UInt8)((fg) >> 16));
		fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
		fg_c = ((UInt8)(fg));
#endif
		x++;
		goto redraw;
	}
	if (GET_BIT(font->Effects, FONT_FX_BOLD))
	{
		g_DrawImageTrans(canvas, x + 1, y, font->pBitmap, bg); 
	}

    return font->pBitmap->width + i;
}

//Bill test
/* Draw the special char on canvas */
int g_DrawAsciiFont(const TCanvas *canvas, const TFont *font, int x, int y, int serial)
{
    long offset;
    UInt8 *pack, bits;
	int fg, bg;
    int i;

	int octanSize;
	int exceeding;

#if defined __RGB16
    
	UInt16 *data;       // point to the data of bitmap

	#define SET_PIXEL_BY_BIT(bit)	\
		(*(data++) = (UInt16) ((bits & (bit)) ? fg : bg))

#elif defined __RGB24

	UInt8 *data; 
	UInt8 fg_a, fg_b, fg_c, bg_a, bg_b, bg_c;

	#define SET_PIXEL_BY_BIT(bit)									\
		if (bits & (bit))											\
		{ *(data++) = fg_a; *(data++) = fg_b; *(data++) = fg_c; }	\
		else														\
		{ *(data++) = bg_a; *(data++) = bg_b; *(data++) = bg_c; }

#endif	

//	ASSERT_DEBUG(canvas->Type && font->pBitmap);

    // Verify the serial number
    if (serial < 0 || serial >= font->TotalNum) 
    {
        TRACE("FontDraw: out of the scope 0-%d! (%d)\n", font->TotalNum - 1, serial);
        return 0;
    }


	if (font->pWidthTbl)
	{
		offset = font->pOffsetTbl[serial];

		font->pBitmap->width  = font->pWidthTbl[serial];
		font->pBitmap->height = font->Height;
	}
	else
	{
		offset = (long) serial * font->PackedSize;

		font->pBitmap->width  = font->Width;
		font->pBitmap->height = font->Height;
	}

    if (font->pFile != NULL)
    {
        // From font file
        fs_Seek(font->pFile, sizeof(FONT_FILE_HEADER) + offset, SEEK_SET);
        fs_Read(font->pCache, sizeof(char) * font->PackedSize, font->pFile);

        offset = 0;		// so later access from pCache
    }


	if (GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{
		fg = font->GLColor;
		x--;
	}
	else
		fg = font->FGColor;

	if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
		bg = font->BGColor;
	else
		bg = fg ^ 0xC0C0;

#ifdef __RGB24
	fg_a = ((UInt8)((fg) >> 16));
	fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
	fg_c = ((UInt8)(fg));
	bg_a = ((UInt8)((bg) >> 16));
	bg_b = ((UInt8)(((UInt16)(bg)) >> 8));
	bg_c = ((UInt8)(bg));
#endif

redraw:


    pack = (UInt8 *)font->pCache + offset;


#if defined __RGB16
    data = (UInt16*) font->pBitmap->data;
#elif defined __RGB24
    data = font->pBitmap->data;
#endif

	if (font->pWidthTbl)
	{
		i = font->pBitmap->width * font->Height;		// length by bits

		octanSize  = i >> 3;	// i / 8;				// how many full 8 bits
		exceeding  = i & 7;		// i % 8;				// exceeding bits
	}
	else
	{
		octanSize  = font->OctanSize;
		exceeding  = font->Exceeding;
	}

	// every full 8 bits
    for (i = 0; i < octanSize; i++)
    {
        bits = *(pack++);

        // Test each bit patterns
		SET_PIXEL_BY_BIT(0x80);
		SET_PIXEL_BY_BIT(0x40);
		SET_PIXEL_BY_BIT(0x20);
		SET_PIXEL_BY_BIT(0x10);
		SET_PIXEL_BY_BIT(0x08);
		SET_PIXEL_BY_BIT(0x04);
		SET_PIXEL_BY_BIT(0x02);
		SET_PIXEL_BY_BIT(0x01);
    }
	// and remained bits
    if (exceeding)
    {
        bits = *pack;
		
        // Test each bit patterns
		for (i = 7; i > 7 - exceeding; i--)
        {
			SET_PIXEL_BY_BIT(1 << i);
        }
    }

    // Draw this pBitmap to canvas
    if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
        g_DrawImage(canvas, x, y, font->pBitmap); 
    else
        g_DrawImageTrans(canvas, x, y, font->pBitmap, bg); 

	i = GET_BIT(font->Effects, FONT_FX_BOLD) ? 1 : 0;

	if (fg != font->FGColor) // && GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{		
		g_DrawImageTrans(canvas, x + 1, y - 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 1, y + 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 2 + i, y, font->pBitmap, bg); 
		if (i)
		{
			g_DrawImageTrans(canvas, x + 2, y - 1, font->pBitmap, bg); 
			g_DrawImageTrans(canvas, x + 2, y + 1, font->pBitmap, bg); 
		}

		fg = font->FGColor;

#ifdef __RGB24
		fg_a = ((UInt8)((fg) >> 16));
		fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
		fg_c = ((UInt8)(fg));
#endif
		x++;
		goto redraw;
	}
	if (GET_BIT(font->Effects, FONT_FX_BOLD))
	{
		g_DrawImageTrans(canvas, x + 1, y, font->pBitmap, bg); 
	}

    return font->pBitmap->width + i;
}

/* Draw the special char on canvas w/ rotate */
int g_DrawFontR(const TCanvas *canvas, const TFont *font, int x, int y, int serial, int rotate)
{
    long offset;
    UInt8 *pack;
	int fg, bg;
    int i, j;

	int exceeding;

#if defined __RGB16
    
	UInt16 *data;       // point to the data of bitmap

	#define SET_PIXEL_BY_BIT_(cond)	\
		(*(data++) = (UInt16) ((cond) ? fg : bg))

#elif defined __RGB24

	UInt8 *data; 
	UInt8 fg_a, fg_b, fg_c, bg_a, bg_b, bg_c;

	#define SET_PIXEL_BY_BIT_(cond)									\
		if ((cond))											\
		{ *(data++) = fg_a; *(data++) = fg_b; *(data++) = fg_c; }	\
		else														\
		{ *(data++) = bg_a; *(data++) = bg_b; *(data++) = bg_c; }

#endif	

//	ASSERT_DEBUG(canvas->Type && font->pBitmap);

    // Verify the serial number
    if (serial < 0 || serial >= font->TotalNum) 
    {
        TRACE("FontDraw: out of the scope 0-%d! (%d)\n", font->TotalNum - 1, serial);
        return 0;
    }

    // Load packed font data to buffer
#if __FONT_CACHE_USE_PAGE_SWAP__
    // find the offset in pCache (directly load from pCache or open font file)
    // and the width and height for pBitmap

	offset = g_LoadFontFromCache(font, serial); // get address of font

	if (offset == 0) // load font fail
	{
		TRACE("FontDraw: load font (serial: %d) from file to page fail.\n");
		return 0;
	}
		
	// set pBitmap width and height
	if (font->pWidthTbl)
	{
		font->pBitmap->width  = font->pWidthTbl[serial];
		font->pBitmap->height = font->Height;
	}
	else
	{
		font->pBitmap->width  = font->Width;
		font->pBitmap->height = font->Height;
	}
#else
	if (font->pWidthTbl)
	{
		offset = font->pOffsetTbl[serial];

		font->pBitmap->width  = font->Height;
		font->pBitmap->height = font->pWidthTbl[serial];
	}
	else
	{
		offset = (long) serial * font->PackedSize;

		font->pBitmap->width  = font->Height;
		font->pBitmap->height = font->Width;
	}

    if (font->pFile != NULL)
    {
        // From font file
        fs_Seek(font->pFile, sizeof(FONT_FILE_HEADER) + offset, SEEK_SET);
        fs_Read(font->pCache, sizeof(char) * font->PackedSize, font->pFile);

        offset = 0;		// so later access from pCache
    }
#endif

	if (GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{
		fg = font->GLColor;
		x--;
	}
	else
		fg = font->FGColor;

	if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
		bg = font->BGColor;
	else
		bg = fg ^ 0xC0C0;

#ifdef __RGB24
	fg_a = ((UInt8)((fg) >> 16));
	fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
	fg_c = ((UInt8)(fg));
	bg_a = ((UInt8)((bg) >> 16));
	bg_b = ((UInt8)(((UInt16)(bg)) >> 8));
	bg_c = ((UInt8)(bg));
#endif
	
redraw_:

    // Unpack this font to pBitmap
#if __FONT_CACHE_USE_PAGE_SWAP__
	pack = (UInt8 *)offset;
#else
	pack = (UInt8 *)font->pCache + offset;
#endif

#if defined __RGB16
    data = (UInt16*) font->pBitmap->data;
#elif defined __RGB24
    data = font->pBitmap->data;
#endif

	if (rotate == 1)
	{
		for (i = 0; i < font->pBitmap->height; i++)
		{
			for (j = 0; j < font->pBitmap->width; j++)
			{
				exceeding = (font->pBitmap->width - 1 - j) * font->pBitmap->height + i;

				SET_PIXEL_BY_BIT_((*(pack + exceeding / 8)) & (1 << (7 - exceeding % 8)));
			}
		}

	}
	else // if (rotate == -1)
	{
		for (i = 0; i < font->pBitmap->height; i++)
		{
			for (j = 0; j < font->pBitmap->width; j++)
			{
				exceeding = (j + 1) * font->pBitmap->height - 1 - i;

				SET_PIXEL_BY_BIT_((*(pack + exceeding / 8)) & (1 << (7 - exceeding % 8)));
			}
		}
	}

    // Draw this pBitmap to canvas
    if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
        g_DrawImage(canvas, x, y, font->pBitmap); 
    else
        g_DrawImageTrans(canvas, x, y, font->pBitmap, bg); 

	i = GET_BIT(font->Effects, FONT_FX_BOLD) ? 1 : 0;

	if (fg != font->FGColor) // && GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{		
		g_DrawImageTrans(canvas, x + 1, y - 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 1, y + 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 2 + i, y, font->pBitmap, bg); 
		if (i)
		{
			g_DrawImageTrans(canvas, x + 2, y - 1, font->pBitmap, bg); 
			g_DrawImageTrans(canvas, x + 2, y + 1, font->pBitmap, bg); 
		}

		fg = font->FGColor;
#ifdef __RGB24
		fg_a = ((UInt8)((fg) >> 16));
		fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
		fg_c = ((UInt8)(fg));
#endif
		x++;
		goto redraw_;
	}
	if (GET_BIT(font->Effects, FONT_FX_BOLD))
	{
		g_DrawImageTrans(canvas, x + 1, y, font->pBitmap, bg); 
	}

    return font->pBitmap->height + i;
}

//Bill test
/* Draw the special char on canvas w/ rotate */
int g_DrawAsciiFontR(const TCanvas *canvas, const TFont *font, int x, int y, int serial, int rotate)
{
    long offset;
    UInt8 *pack;
	int fg, bg;
    int i, j;

	int exceeding;

#if defined __RGB16
    
	UInt16 *data;       // point to the data of bitmap

	#define SET_PIXEL_BY_BIT_(cond)	\
		(*(data++) = (UInt16) ((cond) ? fg : bg))

#elif defined __RGB24

	UInt8 *data; 
	UInt8 fg_a, fg_b, fg_c, bg_a, bg_b, bg_c;

	#define SET_PIXEL_BY_BIT_(cond)									\
		if ((cond))											\
		{ *(data++) = fg_a; *(data++) = fg_b; *(data++) = fg_c; }	\
		else														\
		{ *(data++) = bg_a; *(data++) = bg_b; *(data++) = bg_c; }

#endif	

//	ASSERT_DEBUG(canvas->Type && font->pBitmap);

    // Verify the serial number
    if (serial < 0 || serial >= font->TotalNum) 
    {
        TRACE("FontDraw: out of the scope 0-%d! (%d)\n", font->TotalNum - 1, serial);
        return 0;
    }

	if (font->pWidthTbl)
	{
		offset = font->pOffsetTbl[serial];

		font->pBitmap->width  = font->Height;
		font->pBitmap->height = font->pWidthTbl[serial];
	}
	else
	{
		offset = (long) serial * font->PackedSize;

		font->pBitmap->width  = font->Height;
		font->pBitmap->height = font->Width;
	}

    if (font->pFile != NULL)
    {
        // From font file
        fs_Seek(font->pFile, sizeof(FONT_FILE_HEADER) + offset, SEEK_SET);
        fs_Read(font->pCache, sizeof(char) * font->PackedSize, font->pFile);

        offset = 0;		// so later access from pCache
    }

	if (GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{
		fg = font->GLColor;
		x--;
	}
	else
		fg = font->FGColor;

	if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
		bg = font->BGColor;
	else
		bg = fg ^ 0xC0C0;

#ifdef __RGB24
	fg_a = ((UInt8)((fg) >> 16));
	fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
	fg_c = ((UInt8)(fg));
	bg_a = ((UInt8)((bg) >> 16));
	bg_b = ((UInt8)(((UInt16)(bg)) >> 8));
	bg_c = ((UInt8)(bg));
#endif
	
redraw_:

    // Unpack this font to pBitmap
#if __FONT_CACHE_USE_PAGE_SWAP__
	pack = (UInt8 *)offset;
#else
	pack = (UInt8 *)font->pCache + offset;
#endif

#if defined __RGB16
    data = (UInt16*) font->pBitmap->data;
#elif defined __RGB24
    data = font->pBitmap->data;
#endif

	if (rotate == 1)
	{
		for (i = 0; i < font->pBitmap->height; i++)
		{
			for (j = 0; j < font->pBitmap->width; j++)
			{
				exceeding = (font->pBitmap->width - 1 - j) * font->pBitmap->height + i;

				SET_PIXEL_BY_BIT_((*(pack + exceeding / 8)) & (1 << (7 - exceeding % 8)));
			}
		}

	}
	else // if (rotate == -1)
	{
		for (i = 0; i < font->pBitmap->height; i++)
		{
			for (j = 0; j < font->pBitmap->width; j++)
			{
				exceeding = (j + 1) * font->pBitmap->height - 1 - i;

				SET_PIXEL_BY_BIT_((*(pack + exceeding / 8)) & (1 << (7 - exceeding % 8)));
			}
		}
	}

    // Draw this pBitmap to canvas
    if (GET_BIT(font->Effects, FONT_FX_OPAQUE))
        g_DrawImage(canvas, x, y, font->pBitmap); 
    else
        g_DrawImageTrans(canvas, x, y, font->pBitmap, bg); 

	i = GET_BIT(font->Effects, FONT_FX_BOLD) ? 1 : 0;

	if (fg != font->FGColor) // && GET_BIT(font->Effects, FONT_FX_OUTLINE))
	{		
		g_DrawImageTrans(canvas, x + 1, y - 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 1, y + 1, font->pBitmap, bg); 
		g_DrawImageTrans(canvas, x + 2 + i, y, font->pBitmap, bg); 
		if (i)
		{
			g_DrawImageTrans(canvas, x + 2, y - 1, font->pBitmap, bg); 
			g_DrawImageTrans(canvas, x + 2, y + 1, font->pBitmap, bg); 
		}

		fg = font->FGColor;
#ifdef __RGB24
		fg_a = ((UInt8)((fg) >> 16));
		fg_b = ((UInt8)(((UInt16)(fg)) >> 8));
		fg_c = ((UInt8)(fg));
#endif
		x++;
		goto redraw_;
	}
	if (GET_BIT(font->Effects, FONT_FX_BOLD))
	{
		g_DrawImageTrans(canvas, x + 1, y, font->pBitmap, bg); 
	}

    return font->pBitmap->height + i;
}



